package com.xhj.mybatis.executor;

import com.xhj.mybatis.executor.statement.StatementHandler;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.mapping.MappedStatement;
import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.session.ResultHandler;
import com.xhj.mybatis.session.RowBounds;
import com.xhj.mybatis.transaction.Transaction;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * SimpleExecutor
 *
 * @author XJks
 * @description 简单执行器
 */
public class SimpleExecutor extends BaseExecutor {

    public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        // 声明 Statement 对象，初始为 null，用于执行 SQL
        Statement stmt = null;
        try {
            // 获取 MyBatis 的全局配置对象
            Configuration configuration = ms.getConfiguration();
            // 创建 StatementHandler，用于准备和执行 SQL 语句
            StatementHandler handler = configuration.newStatementHandler(
                    // 当前执行器
                    this,
                    // 映射的 SQL 语句对象
                    ms,
                    // SQL 语句的参数对象
                    parameter,
                    // 默认的分页参数
                    RowBounds.DEFAULT,
                    // 结果处理器，更新操作不需要
                    null,
                    /**
                     * TODO:
                     *  疑问:这里传入 null，真的对吗？
                     *  回答:
                     *      1.如果在执行 SQL 语句时，没有传入 BoundSql 对象会导致 SQL 语句中的 ？ 无法被正确替换为实际的参数值，从而导致 SQL 语句无法正确执行，这显然是不对的。
                     *      2.但在这里不传入 BoundSql 的原因如下:
                     *          2.1 在 {@link Configuration#newStatementHandler(Executor, MappedStatement, Object, RowBounds, ResultHandler, BoundSql)} 方法内部会重新获取一遍 BoundSql，newStatementHandler 方法会调用 {@link com.xhj.mybatis.executor.statement.BaseStatementHandler#BaseStatementHandler(Executor, MappedStatement, Object, RowBounds, ResultHandler, BoundSql)} 构造方法。
                     *          2.2 在 BaseStatementHandler 的构造方法中，如果传入的 BoundSql 为 null，则会调用 MappedStatement 的 getBoundSql 方法重新获取 BoundSql 对象。
                     */
                    null
            );
            // 预处理 SQL，返回 Statement
            stmt = prepareStatement(handler);
            // 执行更新操作，返回受影响的行数
            return handler.update(stmt);
        } finally {
            // 无论是否异常，都关闭 Statement，释放资源
            closeStatement(stmt);
        }
    }

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // 声明一个 Statement 对象，初始为 null，用于后续执行 SQL
        Statement stmt = null;
        try {
            // 获取全局配置对象
            Configuration configuration = ms.getConfiguration();
            // 创建一个 StatementHandler，用于处理 SQL 语句的准备和执行。TODO:注意这里传入的是 wrapper 对象，目前 wrapper 可能是 this，也可能是 CachingExecutor 对象。
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 从事务中获取数据库连接
            Connection connection = transaction.getConnection();
            // 准备 SQL 语句，返回一个已准备好的 Statement 对象
            stmt = handler.prepare(connection);
            // 设置 SQL 语句的参数
            handler.parameterize(stmt);
            // 执行查询操作，返回结果列表
            return handler.query(stmt, resultHandler);
        } finally {
            // 无论是否发生异常，都要关闭 Statement，释放资源
            closeStatement(stmt);
        }
    }

    /**
     * 该方法用于准备 SQL 执行所需的 Statement 对象
     *
     * @param handler 语句处理器
     * @return 准备好的 Statement 对象
     * @throws SQLException SQL 异常
     */
    private Statement prepareStatement(StatementHandler handler) throws SQLException {
        // 声明一个 Statement 变量，用于后续存储创建的 Statement 对象
        Statement stmt;
        // 获取数据库连接
        Connection connection = transaction.getConnection();
        // 使用 handler 的 prepare 方法，传入数据库连接创建 Statement 对象
        stmt = handler.prepare(connection);
        // 调用 handler 的 parameterize 方法，为 Statement 设置 SQL 参数
        handler.parameterize(stmt);
        // 返回已经准备好的 Statement 对象
        return stmt;
    }
}
